package com.upay.android.util.rest;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.DefaultHttpClient;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

import com.upay.android.util.exception.RestException;
import com.upay.android.util.exception.RestExceptionType;
import com.upay.domain.response.ResponseEnvelope;


public class RestTemplate<T extends Object> {

	/* ---- Constants ---- */
	public final static Character QUESTION_MARK = '?';
	public final static Character EMPERCENT = '&';
	public final static Character EQUALS = '=';
	
	/* ---- Instance Variables ---- */
	
	/* ---- Constructors ---- */
	
	/* ---- Business Methods ---- */
	
	public ResponseEnvelope<T> getForEntity(
		String url, 
		RequestMethod requestMethod, 
		ArrayList<NameValuePair> params, 
		TypeReference<ResponseEnvelope<T>> typeReference) {
		
		try {
			InputStream inputStream = null;
			
			if (RequestMethod.GET.equals(requestMethod)) {
				inputStream = this.sendGet(parseParameters(url, params));
			}
						
			if (RequestMethod.POST.equals(requestMethod)) {
				inputStream = this.sendPost(url, params);
			}
						
			return this.parseJson(inputStream, typeReference);
		} catch (JsonMappingException e) {
			throw new RestException(RestExceptionType.ParseException, "JSON Parsing Exception");
		} catch (JsonParseException e) {
			throw new RestException(RestExceptionType.ParseException, "JSON Parsing Exception");
		} catch (IOException e) {
			throw new RestException(RestExceptionType.ConnectionException, "IO Exception");
		}
	}

	public ResponseEnvelope<T> postForEntity(String url, ArrayList<NameValuePair> params, TypeReference<ResponseEnvelope<T>> typeReference) {
		try {
			InputStream inputStream = this.sendPost(url, params);		
			return this.parseJson(inputStream, typeReference);
		} catch (JsonMappingException e) {
			throw new RestException(RestExceptionType.ParseException, "JSON Parsing Exception");
		} catch (JsonParseException e) {
			throw new RestException(RestExceptionType.ParseException, "JSON Parsing Exception");
		} catch (IOException e) {
			throw new RestException(RestExceptionType.ConnectionException, "IO Exception");
		} catch (IllegalArgumentException e) {
			throw new RestException(RestExceptionType.IllegalArgumentException, "Illegal URL arguments");
		}
	}
	
	private String parseParameters(String url, ArrayList<NameValuePair> params) {
		StringBuilder builder = new StringBuilder(url);
		
		if (!params.isEmpty()) {
			builder.append(QUESTION_MARK);
			Boolean firstPair = Boolean.TRUE;
			for (NameValuePair pair : params) {
				if (!firstPair) {
					builder.append(EMPERCENT);
				}
				builder.append(pair.getName());
				builder.append(EQUALS);
				builder.append(pair.getValue());
				firstPair = Boolean.FALSE;
			}
		}
		
		return builder.toString();
	}
	
	private ResponseEnvelope<T> parseJson(InputStream inputStream, TypeReference<ResponseEnvelope<T>> typeReference)
	    throws JsonMappingException, JsonParseException, IOException {
        JsonFactory factory = new JsonFactory();
		ObjectMapper mapper = new ObjectMapper(factory);
		ResponseEnvelope<T> responseEnvelope = mapper.readValue(inputStream, typeReference);
		inputStream.close();
		return responseEnvelope;
	}
	
	private InputStream sendGet(String url) throws IOException {
		DefaultHttpClient client = new DefaultHttpClient();
		HttpGet request = new HttpGet(url);
		return commit(client, request);
	}
	
	private InputStream sendPost(String url, ArrayList<NameValuePair> params) throws IOException {
		DefaultHttpClient client = new DefaultHttpClient();
		HttpPost request = new HttpPost(url);
		request.setEntity(new UrlEncodedFormEntity(params));
		return commit(client, request);
	}

	private InputStream commit(DefaultHttpClient client, HttpRequestBase request)
			throws IOException {
		InputStream inputStream = null;
		try {
			HttpResponse getResponse = client.execute(request);
			inputStream = getResponse.getEntity().getContent();
		} catch (IOException e) {
			client.getConnectionManager().shutdown();
			throw new IOException(e.getMessage());
		}
		return inputStream;
	}


	
	/* ---- Getters and Setters ---- */
	
}
